/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#ifndef __MATH_ANGLES_H__
#define __MATH_ANGLES_H__


// Angle indexes
#define	PITCH						0		// Up / Down
#define	YAW							1		// Left / Right
#define	ROLL						2		// Fall over

#define AngleMod(a)					((360.0f/65536.0f) * (M_FtolFast((a) * (65536.0f/360.0f)) & 65535))

extern odAngles		angles_zero;

class odAngles {
public:
	float			pitch;
	float			yaw;
	float			roll;

					odAngles (void);
	explicit		odAngles (float pitch, float yaw, float roll);
	explicit		odAngles (const odVec3 &vec);

	float			operator [] (int index) const;
	float &			operator [] (int index);

	odAngles &		operator = (const odAngles &angles);
	bool			operator == (const odAngles &angles) const;
	bool			operator != (const odAngles &angles) const;

	odAngles		operator - () const;

	odAngles		operator + (const odAngles &angles) const;
	odAngles		operator - (const odAngles &angles) const;
	odAngles		operator * (float scale) const;
	odAngles		operator / (float div) const;
	odAngles &		operator += (const odAngles &angles);
	odAngles &		operator -= (const odAngles &angles);
	odAngles &		operator *= (float scale);
	odAngles &		operator /= (float div);

	bool			Compare (const odAngles &angles) const;
	bool			Compare (const odAngles &angles, float epsilon = 0.0f) const;
	void			Set (float pitch, float yaw, float roll);
	void			Clear (void);
	bool			IsCleared (void) const;
	void			Clamp (const odAngles &min, const odAngles &max);
	void			Clamp (float min, float max);
	void			Lerp (const odAngles &from, const odAngles &to, float frac);
	void			Normalize360 (void);
	void			Normalize180 (void);

	const float *	ToFloatPtr (void) const;
	float *			ToFloatPtr (void);

	void			ToVectors (odVec3 *forward = NULL, odVec3 *right = NULL, odVec3 *up = NULL) const;
	odMat3			ToMat3 (void) const;
	const char *	ToString (int precision = 2) const;
};

INLINE odAngles::odAngles (void){

}

INLINE odAngles::odAngles (float pitch, float yaw, float roll){

	this->pitch = pitch;
	this->yaw = yaw;
	this->roll = roll;
}

INLINE odAngles::odAngles (const odVec3 &vec){

	this->pitch = vec.x;
	this->yaw = vec.y;
	this->roll = vec.z;
}

INLINE float odAngles::operator [] (int index) const {

	return (&pitch)[index];
}

INLINE float &odAngles::operator [] (int index){

	return (&pitch)[index];
}

INLINE odAngles &odAngles::operator = (const odAngles &angles){

	pitch = angles.pitch;
	yaw = angles.yaw;
	roll = angles.roll;

	return *this;
}

INLINE bool odAngles::operator == (const odAngles &angles) const {

	if (pitch != angles.pitch || yaw != angles.yaw || roll != angles.roll)
		return false;

	return true;
}

INLINE bool odAngles::operator != (const odAngles &angles) const {

	if (pitch == angles.pitch && yaw == angles.yaw && roll == angles.roll)
		return false;

	return true;
}

INLINE odAngles odAngles::operator - () const {

	return odAngles(-pitch, -yaw, -roll);
}

INLINE odAngles odAngles::operator + (const odAngles &angles) const {

	return odAngles(pitch + angles.pitch, yaw + angles.yaw, roll + angles.roll);
}

INLINE odAngles odAngles::operator - (const odAngles &angles) const {

	return odAngles(pitch - angles.pitch, yaw - angles.yaw, roll - angles.roll);
}

INLINE odAngles odAngles::operator * (float scale) const {

	return odAngles(pitch * scale, yaw * scale, roll * scale);
}

INLINE odAngles odAngles::operator / (float div) const {

	float	scale = 1.0f / div;

	return odAngles(pitch * scale, yaw * scale, roll * scale);
}

INLINE odAngles &odAngles::operator += (const odAngles &angles){

	pitch += angles.pitch;
	yaw += angles.yaw;
	roll += angles.roll;

	return *this;
}

INLINE odAngles &odAngles::operator -= (const odAngles &angles){

	pitch -= angles.pitch;
	yaw -= angles.yaw;
	roll -= angles.roll;

	return *this;
}

INLINE odAngles &odAngles::operator *= (float scale){

	pitch *= scale;
	yaw *= scale;
	roll *= scale;

	return *this;
}

INLINE odAngles &odAngles::operator /= (float div){

	float	scale = 1.0f / div;

	pitch *= scale;
	yaw *= scale;
	roll *= scale;

	return *this;
}

INLINE bool odAngles::Compare (const odAngles &angles) const {

	if (pitch != angles.pitch || yaw != angles.yaw || roll != angles.roll)
		return false;

	return true;
}

INLINE bool odAngles::Compare (const odAngles &angles, float epsilon) const {

	if (M_Fabs(pitch - angles.pitch) > epsilon)
		return false;
	if (M_Fabs(yaw - angles.yaw) > epsilon)
		return false;
	if (M_Fabs(roll - angles.roll) > epsilon)
		return false;

	return true;
}

INLINE void odAngles::Set (float pitch, float yaw, float roll){

	this->pitch = pitch;
	this->yaw = yaw;
	this->roll = roll;
}

INLINE void odAngles::Clear (void){

	pitch = 0.0f;
	yaw = 0.0f;
	roll = 0.0f;
}

INLINE bool odAngles::IsCleared (void) const {

	if (pitch != 0.0f || yaw != 0.0f || roll != 0.0f)
		return false;

	return true;
}

INLINE void odAngles::Clamp (const odAngles &min, const odAngles &max){

	if (pitch < min.pitch)
		pitch = min.pitch;
	else if (pitch > max.pitch)
		pitch = max.pitch;

	if (yaw < min.yaw)
		yaw = min.yaw;
	else if (yaw > max.yaw)
		yaw = max.yaw;

	if (roll < min.roll)
		roll = min.roll;
	else if (roll > max.roll)
		roll = max.roll;
}

INLINE void odAngles::Clamp (float min, float max){

	if (pitch < min)
		pitch = min;
	else if (pitch > max)
		pitch = max;

	if (yaw < min)
		yaw = min;
	else if (yaw > max)
		yaw = max;

	if (roll < min)
		roll = min;
	else if (roll > max)
		roll = max;
}

INLINE void odAngles::Lerp (const odAngles &from, const odAngles &to, float frac){

	int		i;

	if (frac <= 0.0f){
		*this = from;
		return;
	}

	if (frac >= 1.0f){
		*this = to;
		return;
	}

	for (i = 0; i < 3; i++){
		if (to[i] - from[i] > 180.0f){
			(*this)[i] = from[i] + ((to[i] - 360.0f) - from[i]) * frac;
			continue;
		}
		if (to[i] - from[i] < -180.0f){
			(*this)[i] = from[i] + ((to[i] + 360.0f) - from[i]) * frac;
			continue;
		}

		(*this)[i] = from[i] + (to[i] - from[i]) * frac;
	}
}

INLINE void odAngles::Normalize360 (void){

	int		i;

	for (i = 0; i < 3; i++)
		(*this)[i] = (360.0f/65536.0f) * (M_FtolFast((*this)[i] * (65536.0f/360.0f)) & 65535);
}

INLINE void odAngles::Normalize180 (void){

	int		i;

	for (i = 0; i < 3; i++){
		(*this)[i] = (360.0f/65536.0f) * (M_FtolFast((*this)[i] * (65536.0f/360.0f)) & 65535);

		if ((*this)[i] > 180.0f)
			(*this)[i] -= 360.0f;
	}
}

INLINE const float *odAngles::ToFloatPtr (void) const {

	return &pitch;
}

INLINE float *odAngles::ToFloatPtr (void){

	return &pitch;
}


#endif	// __MATH_ANGLES_H__
